<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>javascript面向对象编程：封装</title>
    </head>
    <body>
    <a href="http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html">参考文档</a>
    <h3>javascript是一种动态的、弱类型（var）、基于原型的脚本语言。一切皆对象。不允许访问本地硬盘。跨平台。最常用的是客户端javascript；服务器端javascript：当Google的javascript引擎V8出现后，出现了Node.js语言。</h3>
    <ul>
    	<li><i>javascript基于对象，但却不是真正的面向对象编程的语言，因为没有class类概念。</i></li>
    	<li>1.1生成实例对象的原始模式（简单的封装）：如将‘猫’看成对象，然后增加属性和方法。<br>
    	<code>
    		var Cat={name:'',color:''}; //原型对象
    		根据上面的原型对象规格（schema），生成两个实例对象。
    		var cat1={name:'cat1',color:'red'};
    		var cat2={name:'cat2',color:'red'};
    	</code><br>
    	<strong>缺点：多个实例，就重复写多少遍；实例与原型之间，看不出关系。</strong>
    	</li>
    	<li>1.2原始模式改进：<br>
			<code>
				function Cat(name,color){
					return {
						name:name,
						color:color
					}
				}
				//生成实例   等于在调用函数
				var cat1=Cat('cat1','red');
				var cat2=Cat('cat2','red');
			</code><br>
			<strong>确定：两个实例之间看不出内在联系。</strong>
    	</li>
    	<li>1.3构造函数(contructor:一个内部使用this的普通函数，使用new生成实例)模式：<br>
    	<code>
    		function Cat(name,color){
    			this.name=name,
    			this.color:color
    		}
    		//生成实例 实例都含有contructor属性==Cat instanceof运算符检测是否是某个对象的实例。
    		var cat1=new Cat('cat1','red');
    		vat cat2=new Cat('cat2','red'); 
    	</code>
    	<br>
    	<strong>缺点：如果为Cat添加其他属性如：type eat() 这些对于cat1 cat2都是相同的东西。每次生成实例就会复制一份，重复内容占用内存，缺乏效率。</strong></li>
    	<li>1.4Prototype模式：<br>javascript规定，每个构造函数都有一个prototype属性，只想另一个对象。这个对象的所有方法都会被构造函数全部的实例继承。
    	<br><code>
    		Cat.prototype.type="猫科";
    		Cat.prototype.eat=function(){console.log('eat food')};
    	</code><br>
    	<strong>prototype中的属性方法都是同一个地址。<br>
    	验证方法：isPrototypeOf() hasOwnProperty() in运算符</strong></li>
    </ul>
    	<script>
    		function Cat(name,color){
    			// 特权属性（公有属性）
    			this.name=name,
    			this.color=color,
    			// 特权方法（公有方法）
    			// 能被外部访问对的方法。每次实例化都要重新构造，而prototype是原型共享
    			// 在公有方法中可以访问私有变量
    			this.say=function(){  //构造函数内的方法 
    				console.log(this.name+' say hello'+nose);
    			};
    			// 私有属性 通过公有方法来进行操作
    			var nose='一个';
    			var that=this;
    			// 私有方法 
    			function sayName(){
    				alert(that.name);
    			};
    			// 私有和特权属性在函数内部，在构造函数的每个失利中都会包含同样的私有和特权成员副本
    			// 因而实例越多占的内存越多

    		}
    		console.log(Cat.prototype);
    		Cat.prototype.type="猫科";
    		// 公有方法
    		// 向prototype添加新成员会将新方法添加到构造函数的地层中去
    		Cat.prototype.eat=function(){  //prototype上的方法
    			console.log(this.name+' eat food')
    		};
    		// 静态属性 适用于对象的特殊实例
    		// 访问时：需要访问该实例的构造函数然后再是静态属性
    		// 如：cat1.constructor.size
    		Cat.size='bigger';
    		// 静态方法
    		Cat.saySize=function(){
    			alert(this.size);
    		};
    		// 实例化
    		var cat1=new Cat('小红猫','red');
    		var cat2=new Cat('大猫','red');
    		// 测试
    		console.log('两个实例的同值公有属性比较：'+(cat1.color===cat2.color));
    		console.log('两个实例的公有方法比较：'+(cat1.say===cat2.say));
    		console.log('两个实例的prototype方法比较：'+(cat1.type===cat2.type));
    		// 测试属性
    		console.log(Cat.size); //bigger
    		console.log(cat1.size); //undefined 一般实例不能直接访问静态属性
    		console.log(cat1.constructor.size); //bigger 先访问构造函数在访问静态属性
    		console.log(Cat.color); //undefined  构造函数中this不是值Cat而是指向实例化对象
    		console.log(cat1.color); //red this指向实例化对象cat1
    		//测试方法 同上
    		// Cat.saySize();
    		//测试prototype
    		console.log(cat1.prototype);//undefined 实例对象没有prototype 
    		console.log(Cat.prototype);//Object Cat{}
    	</script>
    </body>
</html>